﻿#region COPYRIGHT© 2009-2012 Phillip Clark. All rights reserved.

// For licensing information see License.txt (MIT style licensing).

#endregion

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using FlitBit.Cache.Config;
using FlitBit.Core;

namespace FlitBit.Cache
{
	/// <summary>
	///   Options for MRU caches
	/// </summary>
	public sealed class MruCacheOptions
	{
		int _cacheDepth = NamedCacheConfigElement.CDefaultCacheDepth;
		TimeSpan _cacheItemLifespan = TimeSpan.Parse(NamedCacheConfigElement.CDefaultCacheItemLifespan);
		double _concurrentEvictionFactor = NamedCacheConfigElement.CDefaultConcurrentEvictionFactor;
		IEnumerable<string> _contextKeys = Enumerable.Empty<string>();
		int _evictionsPerSynchronizedWorker = NamedCacheConfigElement.CDefaultEvictionsPerSynchronizedWorker;
		double _synchronizedEvictionFactor = NamedCacheConfigElement.CDefaultSynchronizedEvictionFactor;

		/// <summary>
		///   Gets the cache's depth; after which evictions will be scheduled/processed.
		/// </summary>
		public int CacheDepth
		{
			get
			{
				Contract.Ensures(Contract.Result<int>() > 0);

				return _cacheDepth;
			}
			set
			{
				Contract.Requires<ArgumentOutOfRangeException>(value > 0);

				_cacheDepth = value;
			}
		}

		/// <summary>
		///   Gets a cache item's lifespan.
		/// </summary>
		public TimeSpan CacheItemLifespan { get { return _cacheItemLifespan; } set { _cacheItemLifespan = value; } }

		/// <summary>
		///   Factor that determines when concurrent eviction threads are
		///   launched.
		/// </summary>
		/// <remarks>
		///   This factor is combined with a cache's depth in order to
		///   determine the cache threshold. When the threshold is exceeded
		///   concurrent eviction workers will be started to remove the
		///   least-recently-used items from the cache.
		/// </remarks>
		public double ConcurrentEvictionFactor
		{
			get
			{
				Contract.Ensures(Contract.Result<double>() > 0);
				Contract.Ensures(Contract.Result<double>() <= SynchronizedEvictionFactor);

				return _concurrentEvictionFactor;
			}
			set
			{
				Contract.Requires<ArgumentOutOfRangeException>(value > 0);
				Contract.Requires<ArgumentOutOfRangeException>(value <= SynchronizedEvictionFactor);

				_concurrentEvictionFactor = value;
			}
		}

		/// <summary>
		///   Gets the cache's context keys.
		/// </summary>
		public IEnumerable<string> ContextKeys
		{
			get
			{
				Contract.Ensures(Contract.Result<IEnumerable<string>>() != null);

				return _contextKeys;
			}
			set { this._contextKeys = value == null ? Enumerable.Empty<string>() : value.ToReadOnly(); }
		}

		/// <summary>
		///   Number of evictions processed per synchronized eviction event.
		/// </summary>
		/// <remarks>
		///   If the synchronization threshold is exceeded then a calling
		///   thread is borrowed to process evictions. This setting
		///   indicates the max number of evictions the borrowed thread
		///   should process before returning the thread to the caller.
		/// </remarks>
		public int EvictionsPerSynchronizedWorker
		{
			get
			{
				Contract.Ensures(Contract.Result<int>() > 0);

				return _evictionsPerSynchronizedWorker;
			}
			set
			{
				Contract.Requires<ArgumentOutOfRangeException>(value > 0);
				_evictionsPerSynchronizedWorker = value;
			}
		}

		/// <summary>
		///   Factor that determines when evictions become synchronous.
		/// </summary>
		/// <remarks>
		///   This factor is combined with a cache's depth in order to
		///   determine the cache synchronization threshold. When the
		///   threshold is exceeded the caller's thread will be borrowed
		///   to process evictions.
		/// </remarks>
		public double SynchronizedEvictionFactor
		{
			get
			{
				Contract.Ensures(Contract.Result<double>() >= ConcurrentEvictionFactor);

				return _synchronizedEvictionFactor;
			}
			set
			{
				Contract.Requires<ArgumentOutOfRangeException>(value >= ConcurrentEvictionFactor);

				_synchronizedEvictionFactor = value;
			}
		}
	}
}